[Amazon SageMaker] 組み込みアルゴリズムのオブジェクト検出(ResNet-50)をMac上のMXNetで利用してみました
1 はじめに
CX事業本部の平内(SIN)です。
Amazon SageMake(以下、SageMaker)の組み込みアルゴリズムであるオブジェクト検出は、デバイスにインストールしたMXNetフレームワークの上で利用することが可能です。
今回は、MacOSにセットアップしたMXNetでこれを利用してみました。
最初に、動作している様子です。 ResNet-50をベースネットワークとしたのもで5つの商品だけを検出する小さなモデルですが、比較的高速に動作していると思いました。(Macのファンは、少し唸ってますが・・・)
2 データセット
(1) Ground Truth
元となるデータは、ツールで自動的にアノテーションを付加した、Amazon SageMaker Ground Truth(以下、Ground Truth)形式のものです。
参考:[Amazon SageMaker] 撮影と同時にアノテーションを追加してAmazon SageMaker Ground Truth形式のデータを作成してみました
各商品(5種類)は、それぞれ約80枚撮影し、全部で401枚となっています。
(2) データ増幅
アノテーションがそのまま利用できる範囲で、画像を変換し、データは増幅されています。
参考:[Amazon SageMaker] Amazon SageMaker Ground Truth で作成したデータをOpenCVで増幅してみました
変換に使用したリストは以下の通りで、9種類の変換を追加したことで、データ数は、401件から4011件となりました。
# 変換リスト convertList = [ {"function": saturation, "param": 0.6}, # 彩度 {"function": saturation, "param": 0.8}, {"function": brightness, "param": 0.8}, # 明度 {"function": contrast, "param": 0.8}, # コントラスト {"function": contrast, "param": 1.6}, # コントラスト {"function": contrast, "param": 2.0}, # コントラスト {"function": mosaic, "param": 0.5},# モザイク {"function": gaussian, "param": 10.0},# ガウスノイズ {"function": noise, "param": 0.01}, # ごま塩ノイズ ]
全データ: 401件 増幅後データ: 4011件
(3) RecordIO形式
効率的に学習に回せるように、上記のデータは、RecordIO形式に変換されています。
参考:[Amazon SageMaker] Amazon SageMaker Ground Truth で作成したデータをオブジェクト検出で利用可能なRecordIO形式に変換してみました
学習用と検証用に8対2で分割しています。
全データ: 4010件 [0]ASPARA: 800件 [1]CRATZ: 800件 [2]PRETZEL: 800件 [3]PRIME: 800件 [4]OREO: 810件 ASPARA => 640:160 残り:3210件 CRATZ => 640:160 残り:2410件 PRETZEL => 640:160 残り:1610件 PRIME => 640:160 残り:810件 OREO => 648:162 残り:0件 Train: 3208件 . ├── train.idx ├── train.lst ├── train.rec <= 657.8M ├── validation.idx ├── validation.lst └── validation.rec <= 171.0M
出力された、train.recとvalidation.recは、S3にアップロードされます。
3 オブジェクト検出
学習は、組み込みアルゴリズムのオブジェクト検出(Object Detection)で行われています。
使用したインスタンスは、ml.p3.8xlarge ✕ 1(ボリュームサイズ 50GB)で、epochを50回して、トレーニング時間は、1622秒でした。
使用したハイパーパラメータは、以下のとおりです。
base_network resnet-50 early_stopping false epochs 50 image_shape 512 label_width 350 learning_rate 0.001 lr_scheduler_factor 0.1 mini_batch_size 16 momentum 0.9 nms_threshold 0.45 num_classes 5 num_training_samples 3208 optimizer sgd overlap_threshold 0.5 use_pretrained_model 1 weight_decay 0.0005
学習の経過です。
epoch mAP smooth_l1 cross_entropy ----------------------------------------- 1 0.064 0.54 1.083 10 0.127 0.172 0.731 20 0.307 0.09 0.535 30 0.571 0.06 0.414 40 0.763 0.047 0.325 49 0.807 0.042 0.282
出力されたモデル(model.tar.gz)のサイズは、99.6MByteとなっていました。
4 MXNet用への変換
上記で作成したモデルを、MXNetのフレームワーク上で利用する場合、変換(損失層の削除とNMS層を追加)が必要になります。
変換は、https://github.com/zhreshold/mxnet-ssdのdeploy.pyを利用しました。
また、このツールは、Python2系での実行が必要ということで、作業は、SageMakerのノートブックインスタンス(JupyterNotebook)で行いました。
作業した内容は、以下のとおりです。
# 元モデル(model.tar.gz)を作業フォルダ(trained-model)に展開する TMP_FOLDER='trained-model' !tar -xvzf $TMP_FOLDER/model.tar.gz -C $TMP_FOLDER/ # ツールをダウンロードして、ヘルプを確認(動作確認) !git clone https://github.com/zhreshold/mxnet-ssd.git !python mxnet-ssd/deploy.py -h # ハイパーパラメータを確認 !cat $TMP_FOLDER/hyperparams.json | jq # ツールによる変換 !python mxnet-ssd/deploy.py --network resnet50 --num-class 5 --nms .45 --data-shape 512 --prefix $TMP_FOLDER/model_algo_1
上記により、作業フォルダには、deploy_model_algo_1-0000.params及び、deploy_model_algo_1-symbol.jsonが生成されます。
// 変換前 trained-model/model_algo_1-symbol.json trained-model/hyperparams.json trained-model/model.tar.gz trained-model/model_algo_1-0000.params // 変換後 trained-model/deploy_model_algo_1-0000.params trained-model/deploy_model_algo_1-symbol.json trained-model/model_algo_1-symbol.json trained-model/hyperparams.json trained-model/model.tar.gz trained-model/model_algo_1-0000.params
5 コード
Mac上で実行したコードです。
変換で生成したモデルを、./modelに置き、Webカメラの画像を入力インターフェースに変換して推論にかけています。
こちらは、Python3で動作しています。
import mxnet as mx import cv2 import numpy as np from collections import namedtuple # Webカメラ DEVICE_ID = 0 WIDTH = 800 HEIGHT = 600 FPS = 24 MODEL_PATH = './model/deploy_model_algo_1' CLASSES = ['ASPARA','CRATZ','PRETZEL','PRIME','OREO'] COLORS = [(128, 0, 0),(0, 128, 0),(0, 0, 128),(128, 128, 0),(0, 128,128)] def main(): # Model Initialize SHAPE = 512 input_shapes=[('data', (1, 3, SHAPE, SHAPE))] Batch = namedtuple('Batch', ['data']) sym, arg_params, aux_params = mx.model.load_checkpoint(MODEL_PATH, 0) mod = mx.mod.Module(symbol=sym, label_names=[], context=mx.cpu()) mod.bind(for_training=False, data_shapes=input_shapes) mod.set_params(arg_params, aux_params) # Video Initialize cap = cv2.VideoCapture (DEVICE_ID) cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT) cap.set(cv2.CAP_PROP_FPS, FPS) width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) fps = cap.get(cv2.CAP_PROP_FPS) print("fps:{} width:{} height:{}".format(fps, width, height)) while True: # カメラ画像取得 _, frame = cap.read() if(frame is None): continue # 入力インターフェースへの画像変換 frame = frame[0 : int(height), 0 : int(height)] # 800*600 -> 600*600 frame = cv2.resize(frame, (SHAPE, SHAPE)) # 600*600 -> 512*512 img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # BGR -> RGB img = img.transpose((2, 0, 1)) # 512,512,3 -> 3,512,512 img = img[np.newaxis, :] # 3,512,512 -> 1,3,512,512 # 推論 mod.forward(Batch([mx.nd.array(img)])) prob = mod.get_outputs()[0].asnumpy() prob = np.squeeze(prob) index = int(prob[:, 0][0]) confidence = prob[0][1] x1 = int(prob[0][2] * SHAPE) y1 = int(prob[0][3] * SHAPE) x2 = int(prob[0][4] * SHAPE) y2 = int(prob[0][5] * SHAPE) # 表示 print("[{}] {}, {}, {}, {}".format(CLASSES[index], x1,y1,x2,y2)) if(confidence > 0.6): # 信頼度 frame = cv2.rectangle(frame,(x1, y1), (x2, y2), COLORS[index],2) frame = cv2.rectangle(frame,(x1, y1), (x1 + 150,y1-20), COLORS[index], -1) label = "{} {:.2f}".format(CLASSES[index], confidence) frame = cv2.putText(frame,label,(x1+2, y1-2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1, cv2.LINE_AA) # 画像表示 cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() if __name__ == '__main__': main()
6 最後に
今回は、オブジェクト検出のモデルを、Mac上で利用してみました。 この要領を使用すると、RaspberryPiや、Jetson NanoもMxNetをインストールして利用可能かも知れません。